fix(generator): idempotent install + a consumer re-sync guide - #39
Merged
Conversation
Makes `rails g docs_kit:install` safe to re-run on a years-old site, so
re-running it becomes the sanctioned upgrade path (rather than sites
fossilizing on the generator output of the day they were created).
## Summary
- create_initializer skips (never clobbers) a site's edited
config/initializers/docs_kit.rb; prints a diff hint at the gem template.
- add_routes is now quote/syntax tolerant: a route the site already drew
(single vs double quotes, `to:` vs `=>`) is skipped, not duplicated.
- register_stimulus_controller skips when the docs_kit path is already
wired via EITHER loader (eager OR lazy) — no more double-registration.
- New `--sync` flag: runs only the additive/wiring steps (routes,
initializer hint, importmap/Stimulus, AGENTS.md, .rubocop.yml), never
re-scaffolds site-owned content, and prints a conservative drift
checklist (hand-written render_page, dead IconHelper) — warns, never
auto-deletes. Extracted to a SyncReport helper class.
- README "Keeping a site in sync" section + one-time cleanup table;
CHANGELOG Added/Fixed entries.
## Test Coverage
- route idempotency against a hand-written, differently-styled routes.rb
(single quotes, `to:`, no `.:format`) — no duplicate root/docs#show/search
- create_initializer preserves edited config byte-for-byte on re-run
- --sync scaffolds no site content (registry/pages/CSS) and is idempotent
- --sync drift detection flags render_page + IconHelper, never deletes them
- stimulus: no double-register when the site already lazy-loads docs_kit
## Verification
- [x] bundle exec rubocop passes (103 files)
- [x] bundle exec rspec passes (505 examples, 96.57% line coverage)
- [x] Live --sync vs the gem's own docs/ app: no-op, ZERO drift warnings
- [x] Live --sync vs phlex-reactive/docs: flags render_page + IconHelper,
skips its single-quote routes, no double stimulus registration
Closes #24
Claude-Session: https://claude.ai/code/session_01FPQb6z3YwcKRMbvoJhdxnX
This was referenced Jul 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
docs_kit:installwas unsafe to re-run:create_initializerclobbered a site's edited config, andadd_routesduplicated a route when the site had written it in a different style. Consequence: sites never re-ran it and fossilized on the generator output of the day they were created (the audits found both consumer sites still hand-duplicatingrender_pageand carrying a deadIconHelper).This makes
rails g docs_kit:installfully idempotent — safe on a fresh app AND a years-old site — so re-running it becomes the sanctioned upgrade path as this epic keeps adding routes / initializer lines / AGENTS.md across issues.Closes #24.
Changes
create_initializeradd_routesroute_onceguard matchescontroller#action(orroot) tolerant of single/double quotes,to:vs=>, whitespaceregister_stimulus_controller--syncflagSyncReporthelper: flags a hand-writtenrender_page+ a deadIconHelper— warns, never deletesDesign notes
docs_kit:update) keeps the guard logic in one place.--syncnever re-scaffolds site-owned content (Docregistry, pages, the themedapplication.tailwind.css); those are the site's.SyncReportextracted to its own file (lib/generators/docs_kit/install/sync_report.rb) per the "many small files" rule.Test coverage (TDD — RED first)
routes.rb(single quotes,to:, no.:format): no duplicateroot/docs#show/docs_kit/search#index; the site's own syntax is left untouched.create_initializerpreserves edited config byte-for-byte on re-run + reports the skip/hint.--syncscaffolds no site content (registry/pages/CSS) and is idempotent (a second sync duplicates no routes).--syncdrift detection flagsrender_page+IconHelperand does not delete them.docs_kitpath.Verification gates
bundle exec rspec— 505 examples, 0 failures, 96.57% line coverage (floor is 80%).bundle exec rubocop— clean (103 files).docs-kit newstill runs the fulldocs_kit:install.Live
--syncruns (as the issue requires)1. Against the gem's own
docs/app — a no-op with zero drift warnings ✅The config initializer diff was empty; a second sync produced zero route changes.
2. Against
phlex-reactive/docs— flagsrender_page+IconHelper✅Both drift files were left on disk (warn-only). The consumer's single-quote routes were correctly recognized as already-drawn and not duplicated — the exact bug this issue was about. Its lazy Stimulus registration was preserved, not double-registered (a bug this verification surfaced and this PR also fixes).
Out of scope (per the issue)
--syncthemselves).https://claude.ai/code/session_01FPQb6z3YwcKRMbvoJhdxnX